﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace block_explorer
{
    class SSAOEffect
    {
        private const string sEffectFilename = "Effects/SSAO";

        private const string sSSAOTechniqueName = "SSAO";
        private const string sBlurTechniqueName = "SSAOBlur";
        private const string sBlendTechniqueName = "SSAOBlend";

        private const string sRandomSizeParameterName = "random_size";
        private const string sScreenSizeParameterName = "g_screen_size";
        private const string sSampleRadiusParameterName = "g_sample_rad";
        private const string sIntensityParameterName = "g_intensity";
        private const string sScaleParameterName = "g_scale";
        private const string sBiasParameterName = "g_bias";
        private const string sFarClipParameterName = "FarClip";
        private const string sDepthMapParameterName = "DepthBuffer";
        private const string sNormalMapParameterName = "NormalBuffer";
        private const string sRandomMapParameterName = "RandomMap";
        private const string sInverseProjectionParameterName = "InverseProjection";

        private const string sSSAOMapParameterName = "SSAOBuffer";
        private const string sBlurDirectionParameterName = "BlurDirection";

        private const string sBlurMapParameterName = "Texture";

        private Effect mEffect;

        private EffectTechnique mSSAOTechnique;
        private EffectTechnique mBlurTechnique;
        private EffectTechnique mBlendTechnique;

        private EffectParameter mRandomSize;
        private EffectParameter mScreenSize;
        private EffectParameter mSampleRadius;
        private EffectParameter mIntensity;
        private EffectParameter mScale;
        private EffectParameter mBias;
        private EffectParameter mFarClip;
        private EffectParameter mDepthMap;
        private EffectParameter mNormalMap;
        private EffectParameter mRandomMap;
        private EffectParameter mInverseProjection;

        private EffectParameter mSSAOMap;
        private EffectParameter mBlurDirection;

        private EffectParameter mBlurMap;

        public enum Stages
        {
            SSAO,
            Blur,
            Blend
        }

        public Stages Stage
        {
            set
            {
                switch (value)
                {
                    case Stages.SSAO:
                        mEffect.CurrentTechnique = mSSAOTechnique;
                        break;
                    case Stages.Blur:
                        mEffect.CurrentTechnique = mBlurTechnique;
                        break;
                    case Stages.Blend:
                        mEffect.CurrentTechnique = mBlendTechnique;
                        break;
                }
            }
        }

        public float RandomSize { set { mRandomSize.SetValue(value); } }
        public Vector2 ScreenSize { set { mScreenSize.SetValue(value); } }
        public float SampleRadius { set { mSampleRadius.SetValue(value); } }
        public float Intensity { set { mIntensity.SetValue(value); } }
        public float Scale { set { mScale.SetValue(value); } }
        public float Bias { set { mBias.SetValue(value); } }
        public float FarClip { set { mFarClip.SetValue(value); } }
        public Texture2D DepthMap { set { mDepthMap.SetValue(value); } }
        public Texture2D NormalMap { set { mNormalMap.SetValue(value); } }
        public Texture2D RandomMap { set { mRandomMap.SetValue(value); } }
        public Matrix InverseProjection { set { mInverseProjection.SetValue(value); } }

        public Texture2D SSAOMap { set { mSSAOMap.SetValue(value); } }
        public Vector2 BlurDirection { set { mBlurDirection.SetValue(value); } }

        public Texture2D BlurMap { set { mBlurMap.SetValue(value); } }

        public SSAOEffect(GraphicsDevice device, ContentManager content)
        {
            mEffect = content.Load<Effect>(sEffectFilename);

            mSSAOTechnique = mEffect.Techniques[sSSAOTechniqueName];
            mBlurTechnique = mEffect.Techniques[sBlurTechniqueName];
            mBlendTechnique = mEffect.Techniques[sBlendTechniqueName];

            mRandomSize = mEffect.Parameters[sRandomSizeParameterName];
            mScreenSize = mEffect.Parameters[sScreenSizeParameterName];
            mSampleRadius = mEffect.Parameters[sSampleRadiusParameterName];
            mIntensity = mEffect.Parameters[sIntensityParameterName];
            mScale = mEffect.Parameters[sScaleParameterName];
            mBias = mEffect.Parameters[sBiasParameterName];
            mFarClip = mEffect.Parameters[sFarClipParameterName];

            mDepthMap = mEffect.Parameters[sDepthMapParameterName];
            mNormalMap = mEffect.Parameters[sNormalMapParameterName];
            mRandomMap = mEffect.Parameters[sRandomMapParameterName];

            mInverseProjection = mEffect.Parameters[sInverseProjectionParameterName];

            mSSAOMap = mEffect.Parameters[sSSAOMapParameterName];
            mBlurDirection = mEffect.Parameters[sBlurDirectionParameterName];

            mBlurMap = mEffect.Parameters[sBlurMapParameterName];
        }

        public void Apply()
        {
            mEffect.CurrentTechnique.Passes[0].Apply();
        }
    }
}
